package com.packageProject.util;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.*;
import java.util.List;

@Slf4j
public class FileCopyUtil {
    // 定义大文件的阈值（例如：100MB）
    private static final long LARGE_FILE_THRESHOLD = 100L * 1024 * 1024;  // 100MB
    private static final int BIG_FILE_BUFFER_SIZE = 8192;  // 大文件缓冲区

    /**
     * 拷贝单个文件到目标目录。
     *
     * @param sourceFile             源文件路径
     * @param targetDir              目标目录路径
     * @param overwriteExistingFiles 是否覆盖已存在的文件
     * @throws IOException 如果发生 I/O 错误
     */
    public static void copySingleFile(String sourceFile, String targetDir, boolean overwriteExistingFiles) throws IOException {
        Path sourcePath = Paths.get(sourceFile);
        Path targetDirPath = Paths.get(targetDir);

        if (!Files.exists(sourcePath)) {
            throw new IllegalArgumentException("Source file does not exist: " + sourceFile);
        }

        if (!Files.exists(targetDirPath)) {
            Files.createDirectories(targetDirPath);
            log.debug("create dir success : {}", targetDirPath);
        }

        Path targetFilePath = targetDirPath.resolve(sourcePath.getFileName());

        // 判断是否覆盖已存在的文件
        if (Files.exists(targetFilePath) && !overwriteExistingFiles) {
            log.debug("Skipping existing file: {}", targetFilePath.toString());
            return;
        }

        // 获取文件大小
        long fileSize = Files.size(sourcePath);
        if (fileSize > LARGE_FILE_THRESHOLD) {
            log.debug("Detected large file: {}. Using stream-based copy.", sourcePath.toString());
            copyLargeFile(sourcePath, targetFilePath);
        } else {
            CopyOption[] options = overwriteExistingFiles ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[0];
            Files.copy(sourcePath, targetFilePath, options);
            log.debug("Copied small file from {} to {}", sourcePath.toString(), targetFilePath.toString());
        }
    }

    /**
     * 使用流式拷贝大文件。
     *
     * @param source      源文件路径
     * @param destination 目标文件路径
     * @throws IOException 如果发生 I/O 错误
     */
    private static void copyLargeFile(Path source, Path destination) throws IOException {
        long start = System.currentTimeMillis();
        // 使用 try-with-resources 确保流被正确关闭
        try (InputStream is = new BufferedInputStream(Files.newInputStream(source));
             OutputStream os = new BufferedOutputStream(Files.newOutputStream(destination))) {

            byte[] buffer = new byte[BIG_FILE_BUFFER_SIZE];  // 使用8KB缓冲区
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            // 刷新输出流以确保所有数据都被写出
            os.flush();
        }
        long end = System.currentTimeMillis();
        log.debug("Stream-based copy completed for file: {} ,cost: {} ms", source.toString(), (end - start));
    }

    public static void copyDirectory(String sourceDir, String targetDir) {
        copyDirectory(sourceDir, targetDir, null);
    }

    public static void copyDirectory(List<String> sourceDirList, String targetDir) {
        try {
            for (String sourceDir : sourceDirList) {
                copyDirectory(sourceDir, targetDir, true, true, null);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void copyDirectory(String sourceDir, String targetDir, List<String> fileAllowSuffixList) {
        try {
            copyDirectory(sourceDir, targetDir, false, true, fileAllowSuffixList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将源目录下的所有文件拷贝到目标目录。
     *
     * @param sourceDir              源目录路径
     * @param targetDir              目标目录路径
     * @param includeDirs            是否包含源目录名称作为目标目录中的子目录
     * @param overwriteExistingFiles 是否覆盖已存在的文件
     * @throws IOException 如果发生 I/O 错误
     */
    public static void copyDirectory(String sourceDir, String targetDir, boolean includeDirs, boolean overwriteExistingFiles, List<String> fileAllowSuffixList) throws IOException {
        Path sourcePath = Paths.get(sourceDir);
        Path basePath = includeDirs ? Paths.get(targetDir, sourcePath.getFileName().toString()) : Paths.get(targetDir);

        if (!Files.exists(sourcePath)) {
            throw new IllegalArgumentException("Source directory does not exist: " + sourceDir);
        }

        // 创建目标目录（如果需要）
        if (!Files.exists(basePath)) {
            Files.createDirectories(basePath);
            log.info("Created directory: " + basePath);
        }

        Files.walk(sourcePath)
                .forEach(source -> {
                    try {
                        Path target = basePath.resolve(sourcePath.relativize(source));
                        if (Files.isDirectory(source)) {
                            if (!Files.exists(target)) {
                                Files.createDirectories(target);
                            }
                        } else {
                            // 如果fileAllowSuffixList为null或者文件后缀在列表中，则复制文件
                            if (fileAllowSuffixList == null || fileAllowSuffixList.stream().anyMatch(suffix -> source.toString().endsWith(suffix))) {
                                copySingleFile(source.toString(), target.getParent().toString(), overwriteExistingFiles);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });

        log.info("Directory copied successfully from " + sourceDir + " to " + basePath.toString());
    }

    public static void main(String[] args) throws IOException {
        copyDirectory("C:\\Users\\wy\\Desktop\\新建文件夹", "C:\\Users\\wy\\Desktop\\新建文件夹(2)");
    }
}